package com.zyplayer.doc.manage.framework.interceptor;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.zyplayer.doc.data.config.security.DocUserDetails;
import com.zyplayer.doc.data.config.security.UserAuthInfo;
import com.zyplayer.doc.data.repository.manage.entity.UserInfo;
import com.zyplayer.doc.data.repository.manage.mapper.UserInfoMapper;
import com.zyplayer.doc.data.service.manage.UserAuthService;
import com.zyplayer.doc.data.service.manage.UserInfoService;
import com.zyplayer.doc.data.service.manage.impl.UserInfoServiceImpl;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.security.sasl.AuthenticationException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.Duration;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Optional;

/**
 * @author : caiyi
 * @description : 校验token
 * @createDate : 2024/11/28 15:28
 */
@Component
public class TokenValidator
{
    private final Cache<String, DocUserDetails> docUserDetailsCache;
    @Value("${server.securityAuthority:}")
    private String securityAuthority;
    @Resource
    private UserAuthService userAuthService;
    @Resource
    private UserInfoService userInfoService;

    public TokenValidator()
    {
        this.docUserDetailsCache = Caffeine.newBuilder()
                .expireAfterWrite(Duration.ofMinutes(10))
                .build();
    }

    public DocUserDetails validateToken(String token)
    {
        token = token.replace("Bearer", "").trim();
        String checkTokenUrl = String.format("%soauth/check_token?token=%s", securityAuthority, token);

        DocUserDetails docUserDetails = docUserDetailsCache.getIfPresent(token);

        if (docUserDetails == null) {
            try {
                HttpURLConnection connection = (HttpURLConnection) new URL(checkTokenUrl).openConnection();
                connection.setRequestMethod("GET");

                int responseCode = connection.getResponseCode();
                if (responseCode != 200) {
                    throw new AuthenticationException("Token验证失败");
                }

                BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                StringBuilder response = new StringBuilder();
                String inputLine;
                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();

                CheckTokenRes checkTokenRes = JSON.parseObject(response.toString(), CheckTokenRes.class);//new Gson().fromJson(response.toString(), CheckTokenRes.class);

                String userId = Optional.ofNullable(checkTokenRes.getAuthorities())
                        .flatMap(authorities -> Arrays.stream(authorities)
                                .filter(condition -> condition.startsWith(SecurityAuthClaimTypes.userId_pre))
                                .findFirst())
                        .orElse(null);

                String tenantId = Optional.ofNullable(checkTokenRes.getAuthorities())
                        .flatMap(authorities -> Arrays.stream(authorities)
                                .filter(condition -> condition.startsWith(SecurityAuthClaimTypes.tenantId_pre))
                                .findFirst())
                        .orElse(null);

                String userRealName = Optional.ofNullable(checkTokenRes.getAuthorities())
                        .flatMap(authorities -> Arrays.stream(authorities)
                                .filter(condition -> condition.startsWith(SecurityAuthClaimTypes.realName_pre))
                                .findFirst())
                        .orElse(null);

                //有控制就是信息错误
                if (userId == null || tenantId == null || userRealName == null)
                {
                    return null;
                }
                userId = userId.replace(SecurityAuthClaimTypes.userId_pre, "");
                userRealName = userRealName.replace(SecurityAuthClaimTypes.realName_pre, "");
                tenantId = tenantId.replace(SecurityAuthClaimTypes.tenantId_pre, "");

                Long userIdLong = Long.valueOf(userId);
                Long tenantIdLong = Long.valueOf(tenantId);

                List<UserAuthInfo> userAuthSet = userAuthService.getUserAuthSet(userIdLong);

                //没有用户直接添加
                UserInfo userInDb = userInfoService.getById(userIdLong);
                if(userInDb == null)
                {
                    userInDb = new UserInfo()
                            .setId(userIdLong)
                            .setSex(1)
                            .setUserNo(userRealName)
                            .setUserName(userRealName)
                            .setDelFlag(0)
                            .setCreationTime(new Date())
                            .setUpdateTime(new Date())
                            .setCreateUid(1L)
                            .setPassword(DigestUtils.md5DigestAsHex("Aa123456".getBytes()));
                    userInDb.setTenantId(tenantIdLong);
                    userInfoService.save(userInDb);
                }

                docUserDetails = new DocUserDetails(
                        userIdLong,
                        userRealName,
                        userInDb.getPassword(),
                        true,
                        tenantIdLong,
                        userAuthSet);

                docUserDetailsCache.put(token, docUserDetails);

            } catch (IOException e) {
                return null;
            }
        }

        return docUserDetails;
    }

    @Data
    public static class CheckTokenRes
    {
        private String[] authorities;
        private String[] aud;
        private String user_name;
        private boolean active;
    }

    public static class SecurityAuthClaimTypes {
        public static final String userId_pre = "user_";
        public static final String tenantId_pre = "tenant_";
        public static final String realName_pre = "real_name_";
    }
}
